// 通用的proto双向rpc
package robot

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"errors"
	"math/rand"
	"path"
	"strings"
	"time"

	"device-admin/config"
	"device-admin/config/mysql"
	"device-admin/config/redis"
	"device-admin/internal/app/grpc/protoc/robotc"
	"device-admin/internal/app/grpc/service/alarm_monitor"
	"device-admin/internal/app/grpc/service/common"
	"device-admin/internal/app/grpc/service/pubsub"
	"device-admin/internal/app/grpc/service/terminal"
	"device-admin/internal/app/web/service/device"
	"device-admin/internal/define"
	"device-admin/internal/models"
	"device-admin/internal/pkg/file"

	alarm_service "device-admin/internal/app/web/service/alarm"

	db_sharding "192.168.1.75/go-pkg/db-sharding"
	"192.168.1.75/go-pkg/errorx"
	"192.168.1.75/go-pkg/go-cache"
	"192.168.1.75/go-pkg/helper"
	"192.168.1.75/go-pkg/logx"
	"google.golang.org/protobuf/proto"
)

// validateMac 验证mac
func validateMac(robotID, macAddr string) bool {
	db := mysql.NewDB()
	// 兼容最开始没有网卡的设备，后面可以做成强校验
	if macAddr == "" {
		return true
	}
	// 校验设备是否存在
	var device models.Device
	rowsAffected := db.Model(&models.Device{}).
		Where("robot_id = ?", robotID).
		Take(&device).RowsAffected
	if rowsAffected == 0 {
		logx.Error(context.Background(), "robotID not exists", logx.String("robot_id", robotID))
		return false
	}
	// 当mac未被使用时，保存mac
	if /*device.MacAddr == "" &&*/ macAddr != "" {
		rowsAffected := db.Model(&models.Device{}).
			Where("robot_id = ?", robotID).
			Updates(map[string]interface{}{
				"mac_addr": macAddr,
			}).RowsAffected
		if rowsAffected == 0 {
			logx.Error(context.Background(), "update robot mac failed", logx.String("robot_id", robotID))
			return false
		}
		return true
	} else if device.MacAddr != "" {
		return device.MacAddr == macAddr
	}
	return false
}

// cleanWildMsgSession 清除wilgmsg会话信息
func cleanWildMsgSession(robotID string) {
	rdb := redis.NewDB()
	// 取消订阅，并删除订阅的通道数据
	{
		chanMapKey := "wildmsg@" + robotID + "@95"
		// 取消订阅
		pubsub.CancelSub(robotID, "95")
		// 清理设备命令
		pubsub.RobotCMDChanMap.Delete(chanMapKey)
	}

	// 清除设备注册标记
	deviceRegistered[robotID] = false

	// 停止定时任务并删除map对应的值
	RemoveDeviceSessionCron(robotID)

	// 修改机器最近活跃时间
	mysql.NewDB().Model(&models.Device{}).Where("robot_id = ?", robotID).Updates(&models.Device{
		LatestActiveTime: time.Now().Unix() - 20,
	})

	// 离线检测任务
	// terminal.AddOfflineAlarmCheck(robotID, "95")
	alarm_monitor.AlarmMonitor{}.AddMonitor(robotID, 2001, time.Now().Unix(), 1, false, "", "")

	// 清除Redis实时状态
	redisCtx, redisCtxCancel := context.WithTimeout(context.Background(), time.Second*3)
	defer redisCtxCancel()
	rdb.Del(redisCtx, define.RedisRobotRTInfo+robotID)

	// 回话结束
	logx.Info(context.Background(), "C2SSS_WILD offline", logx.String("robotID", robotID))
}

// pushToSendWildmsgChan
func pushToSendWildmsgChan(sendWildmsgChan chan *robotc.WildMsg, wildmsg *robotc.WildMsg) {
	if len(sendWildmsgChan) < 100 {
		sendWildmsgChan <- wildmsg
	}
}

// C2SSS_WILD 接收或发送消息
func (s *Server) C2SSS_WILD(sess robotc.BaseService_C2SSS_WILDServer) error {
	var finishedInit bool
	// 获取robotID和macAddr
	robotID := GetRobotID(sess.Context())
	macAddr := GetRobotMAC(sess.Context())
	if robotID == "" {
		logx.Error(context.Background(), "C2SSS_WILD", logx.String("error", "robot_id can not be empty"))
		return errors.New("robot_id can not be empty")
	}
	// 初始化检测日志
	time.AfterFunc(time.Second*3, func() {
		if !finishedInit {
			logx.Info(context.Background(), "C2SSS_WILD init failed", logx.String("robot_id", robotID))
		}
	})
	// 会话锁
	logx.Info(context.Background(), "C2SSS_WILD wait lock", logx.String("robot_id", robotID))
	if !terminal.SessionLock(robotID, "95") {
		logx.Error(context.Background(), "repeated C2SSS_WILD", logx.String("robot_id", robotID))
		return errorx.New("repeated C2SSS_WILD", -1)
	}
	logx.Info(context.Background(), "C2SSS_WILD get lock", logx.String("robot_id", robotID))
	defer terminal.SessionUnLock(robotID, "95")

	// 校验mac
	validateMac(robotID, macAddr)
	// if !validateMac(robotID, macAddr) {
	// 	logx.Error(context.Background(), "C2SSS_WILD connect failed,invalid macaddr", logx.String("robot_id", robotID))
	// 	return errors.New("invalid mac addr")
	// }

	// 订阅
	pubsub.AddSub(robotID, "95")
	logx.Info(context.Background(), "C2SSS_WILD sub", logx.String("robot_id", robotID))

	// 自动添加终端
	terminal.AutoAddTerminal(robotID, "95")
	logx.Info(context.Background(), "AutoAddTerminal", logx.String("robot_id", robotID))

	// 退出自动清除会话数据
	defer cleanWildMsgSession(robotID)
	logx.Info(context.Background(), "offline-check", logx.String("robot_id", robotID))

	// redis消息队列
	chanMapKey := "wildmsg@" + robotID + "@95"
	pubsub.RobotCMDChanMap.Store(chanMapKey, make(chan string, 50))
	robotCMDChanInterface, _ := pubsub.RobotCMDChanMap.Load(chanMapKey)
	robotCMDChan, _ := robotCMDChanInterface.(chan string)
	logx.Info(context.Background(), "init pubsub chan", logx.String("robot_id", robotID))

	// 本地消息队列
	localMsgChanMapKey := "wildmsg@" + robotID + "@95"
	pubsub.LocalMsgChannelMap.LoadOrStore(localMsgChanMapKey, make(chan *robotc.WildMsg, 50))
	localWildmsgCMDChannelVal, _ := pubsub.LocalMsgChannelMap.Load(localMsgChanMapKey)
	wildmsgCMDChannel, _ := localWildmsgCMDChannelVal.(chan *robotc.WildMsg)
	logx.Info(context.Background(), "init localpubsub chan", logx.String("robot_id", robotID))

	// 连接日志
	logx.Info(context.Background(), "C2SSS_WILD online", logx.String("robot_id", robotID))
	terminal.RemoveOfflineAlarmCheck(robotID, "95") // 设备上线，移除离线告警检测

	// 缓存实时状态
	func() {
		cache.HSet(define.CacheRobotRTInfo+robotID, "online", 1, 0, nil)
		cache.Expire(define.CacheRobotRTInfo+robotID, time.Second*60)
	}()

	// 获取版本信息
	getSystemVersion(robotID)
	logx.Info(context.Background(), "C2SSS_WILD getSystemVersion", logx.String("robot_id", robotID))

	// 获取配置
	common.RobotConfigReq(robotID, []string{
		"PPOAC_Params.OA_OBS_RADIUS",       // 机器人避障半径
		"MR_Params.RECHARGING_BATT_LO_PER", // 低电量阈值
	})
	logx.Info(context.Background(), "C2SSS_WILD getConfigItem", logx.String("robot_id", robotID))

	// 初始化定时任务
	InitDeviceSessionCrond(robotID)
	logx.Info(context.Background(), "C2SSS_WILD InitDeviceCrond", logx.String("robot_id", robotID))

	// 初始化地图及转换矩阵缓存数据
	InitMapCacheInfo(robotID)
	logx.Info(context.Background(), "C2SSS_WILD InitMapCacheInfo", logx.String("robot_id", robotID))

	// 会话结束控制context
	sessionCtx, sessionCancel := context.WithCancel(context.Background())
	defer sessionCancel()

	// 初始化完成标记
	finishedInit = true

	// sendMsgChan 待发送的msg通道
	sendMsgChan := make(chan *robotc.WildMsg, 100)

	// shell命令
	{
		// 初始化95环境
		InitADBEnv(robotID)
		// 临时解决上位机任务积累过多导致的问题
		TaskReordSchedule(robotID)
		// 自动更新设置95架构类型
		AutoUpdateDevice95Arch(robotID)
	}

	// 监测redis订阅
	go func() {
		tk := time.NewTicker(time.Second * 10)
		defer tk.Stop()
		for {
			select {
			// 会话结束
			case <-sess.Context().Done():
				return
			case <-sessionCtx.Done():
				return
			// 每隔10秒定时发送心跳
			case <-tk.C:
				// 订阅的内容是否丢失
				rdb := redis.NewDB()
				chs, err := rdb.PubSubChannels(context.Background(), "wildmsg@"+robotID+"@95").Result()
				if err != nil && len(chs) <= 0 {
					pubsub.AddSub(robotID, "95")
					logx.Info(context.Background(), "C2SSS_WILD resub channel", logx.String("topic", "wildmsg@"+robotID+"@95"))
				}
				// 会话锁延期
				terminal.ExpireSessionLock(robotID, "95")
			}
		}
	}()
	// 接收处理
	go func() {
		for {
			select {
			// 连接结束
			case <-sess.Context().Done():
				return
			case <-sessionCtx.Done():
				return
			default:
				wildmsg, err := sess.Recv()
				// 接收出现错误，直接结束
				if err != nil {
					sessionCancel()
					return
				}
				// 消息处理
				if wildmsg != nil {
					// 发布消息响应
					PublishCMDRsp(robotID, wildmsg)
					// 消息处理
					s.MsgRevHandler(sess.Context(), wildmsg)
					// 消息ack
					{
						ackMsg := wildMsgAckPack(wildmsg)
						if ackMsg != nil {
							pushToSendWildmsgChan(sendMsgChan, ackMsg)
						}
					}
				}
			}
		}
	}()
	// 发送处理
	for {
		select {
		// 连接结束
		case <-sess.Context().Done():
			return nil
		case <-sessionCtx.Done():
			return nil
		// 向95发送消息
		case msg := <-sendMsgChan:
			// 发送失败，2s后重试1次
			if err := sess.Send(msg); err != nil {
				time.Sleep(time.Second * 2)
				if err := sess.Send(msg); err != nil {
					logx.Error(context.Background(), "send-wildmsg-err", logx.String("robot_id", robotID), logx.Any("msg", msg))
					sessionCancel()
					// 发送失败，则清除本地消息发送缓存,避免积压历史消息
					pubsub.LocalMsgChannelMap.Delete(localMsgChanMapKey)
					return nil
				}
			}

		// 处理本地命令任务
		case msg := <-wildmsgCMDChannel:
			pushToSendWildmsgChan(sendMsgChan, msg)

		// 订阅的命令下发
		case data := <-robotCMDChan:
			wildmsg := &robotc.WildMsg{}
			dataBytes, _ := base64.StdEncoding.DecodeString(data)
			err := proto.Unmarshal(dataBytes, wildmsg)
			if err == nil {
				pushToSendWildmsgChan(sendMsgChan, wildmsg)
			}

		// 超过30秒接收不到设备状态数据，退出wildmsg
		default:
			if _, found := cache.HGet(define.CacheRobotRTInfo+robotID, "online"); !found {
				logx.Info(context.Background(), "receive robotStatus timeout", logx.String("robot_id", robotID))
				sessionCancel()
				return nil
			}
			time.Sleep(time.Millisecond * 100)
		}
	}
}

// MsgHandler 消息处理
func (s *Server) MsgRevHandler(ctx context.Context, msg *robotc.WildMsg) {
	surviveInfo := &robotc.RspGetSurviveInfoClient2Server{}   // 存活信息
	ddrAlarmInfo := &robotc.RspGetDDRAlarmInfoClient2Server{} // 告警信息
	robotID := GetRobotID(ctx)
	switch msg.BodyType {
	// 升级通知响应，更新升级状态
	case string((&robotc.RspStartUpgrade{}).ProtoReflect().Descriptor().FullName()):
		rspStartUpgrade := &robotc.RspStartUpgrade{}
		proto.Unmarshal(msg.BodyContent, rspStartUpgrade)
		if rspStartUpgrade.UpgradeOk {
			terminal.UpdateUpgradeStatus(robotID, "95", 3) // 下载中
		} else {
			terminal.UpdateUpgradeStatus(robotID, "95", 2) // 暂不升级
		}
	// 请求文件传输
	case string((&robotc.ReqTransmissionFile{}).ProtoReflect().Descriptor().FullName()):
		reqTransmissionFile := &robotc.ReqTransmissionFile{}
		proto.Unmarshal(msg.BodyContent, reqTransmissionFile)
		content, totalSize, err := terminal.GetUpgradeContent(string(reqTransmissionFile.FileName), reqTransmissionFile.StartOffset, int64(reqTransmissionFile.PackageSize))
		if err != nil {
			logx.Error(context.Background(), "GetUpgradeContent error", logx.Err(err))
		}
		progress := float64(0)
		if totalSize <= 0 {
			progress = 0
		} else if (reqTransmissionFile.StartOffset + int64(reqTransmissionFile.PackageSize)) <= totalSize {
			progress = float64(reqTransmissionFile.StartOffset+int64(reqTransmissionFile.PackageSize)) / float64(totalSize)
		} else {
			progress = 1
		}
		terminal.UpdateUpgradeProgress(robotID, "95", int(progress*100))
		terminal.SendUpgradeContent(robotID, "95", content, msg.StrGUID, err)
	// 下载结果
	case string((&robotc.ReqTransmissionEnd{}).ProtoReflect().Descriptor().FullName()):
		terminal.UpdateUpgradeStatus(robotID, "95", 4) // 下载结束
	// 开始升级安装
	case string((&robotc.ReqBeginUpgrade{}).ProtoReflect().Descriptor().FullName()):
		terminal.UpdateUpgradeStatus(robotID, "95", 8)
	// 升级结果
	case string((&robotc.ReqUpgradeEndStatus{}).ProtoReflect().Descriptor().FullName()):
		reqUpgradeEndStatus := &robotc.ReqUpgradeEndStatus{}
		if reqUpgradeEndStatus.UpgradeRet == 0 {
			terminal.UpdateUpgradeStatus(robotID, "95", 5) // 升级成功
		} else {
			terminal.UpdateUpgradeStatus(robotID, "95", 6) // 升级失败
		}
	// 告警信息处理
	case string(ddrAlarmInfo.ProtoReflect().Descriptor().FullName()):
		proto.Unmarshal(msg.BodyContent, ddrAlarmInfo)
		ddrAlarmInfoHandle(ctx, ddrAlarmInfo)
	// 存活信息处理
	case string(surviveInfo.ProtoReflect().Descriptor().FullName()):
		proto.Unmarshal(msg.BodyContent, surviveInfo)
		surviveInfoHandle(ctx, surviveInfo)
	// 超声传感器
	case "BackgroundInformationProto.OBSUSIRDataC2S":
		// 发布实时数据
		{
			dateBytes, _ := proto.Marshal(msg)
			dataMd5 := helper.Md5Bytes(dateBytes)
			if _, found := cache.Get(define.CacheOBSUSIRData + GetRobotID(ctx) + dataMd5); !found {
				cache.Set(define.CacheOBSUSIRData+GetRobotID(ctx)+dataMd5, GetRobotID(ctx), time.Second*3, nil)
				cache.HSet(define.CacheRobotRTInfo+GetRobotID(ctx), "OBSUSIRData", msg.BodyContent, 0, nil)
				// go PublishRobotRealtimeInfo(ctx, "obs_usir_data")
			} else {
				cache.Expire(define.CacheOBSUSIRData+dataMd5, time.Second*3)
			}
		}
	// 设备日志
	case "BackgroundInformationProto.OptEventSet":
		optEventSetHandle(ctx, msg.BodyContent)
	// 报警通知
	case "AlarmInfoProto.notifyAlarmContent":
		notifyAlarmContentHandle(ctx, msg.BodyContent)
	// 告警设置
	case "AlarmInfoProto.AlarmSetConfig":
		rd := rand.New(rand.NewSource(time.Now().UnixNano()))
		time.AfterFunc(time.Second*time.Duration(rd.Intn(180)), func() {
			alarmSettingHandle(ctx, msg.BodyContent)
		})
	// 地图数据
	case "DDRGRPCService.NotifyMapData":
		mapData := &robotc.NotifyMapData{}
		if err := proto.Unmarshal(msg.BodyContent, mapData); err == nil {
			notifyMapDataHandle(ctx, mapData)
		}
	// 设备地图列表
	// 删除无用的地图，包含数据库记录和图片文件以及图片扩展数据
	case "DDRGRPCService.notifyMapListMsg":
		notifyMapListMsgHandle(ctx, msg.BodyContent)
	// 任务记录处理
	case "BackgroundInformationProto.rspGetTaskRecordInfoClient2Server":
		taskRecordHandle(ctx, msg.BodyContent)
	// 回路检测
	case "DDRCommProto.rspDetectLoop":
		rspDetectLoopHandle(ctx, msg)
	// 版本信息
	case "DDRCommProto.rspGetSysVersion":
		systemVersionHandle(ctx, msg)
	// 设备状态数据
	case "BackgroundInformationProto.NotifyRobotStatus":
		robotStatus := &robotc.NotifyRobotStatus{}
		if err := proto.Unmarshal(msg.GetBodyContent(), robotStatus); err != nil {
			logx.Error(context.Background(), "BackgroundInformationProto.NotifyRobotStatusErr", logx.String("robot_id", GetRobotID(ctx)), logx.Err(err))
		}
		// 实时状态缓存保持
		cache.Expire(define.CacheRobotRTInfo+robotID, time.Second*60)
		robotStatusHandle(ctx, robotStatus)
	// 云点数据
	case "DDRCommProto.notifyObsData":
		cache.HSet(define.CacheRobotRTInfo+GetRobotID(ctx), "obs_data", msg.GetBodyContent(), 0, nil)
	// 开发日志
	case string((&robotc.NotifyDevEventLog{}).ProtoReflect().Descriptor().FullName()):
		DevEventLogHandle(robotID, msg)
	// 机器开关机时间组
	case string((&robotc.RspSwitchMachineConfig{}).ProtoReflect().Descriptor().FullName()):
		alarm_monitor.SaveBootSchedule(robotID, msg)
	// 95的配置
	case string((&robotc.RspConfigOperational{}).ProtoReflect().Descriptor().FullName()):
		DDRConfigHandler(robotID, msg)
	default: // 其他数据

	}
}

// wildMsgAckPack 消息回复的pack
func wildMsgAckPack(msg *robotc.WildMsg) *robotc.WildMsg {
	msgID := msg.StrGUID
	if !strings.HasPrefix(msgID, "waitmsg_") {
		wm := &robotc.WildMsg{}
		wm.FromType = robotc.WildMsg_eMobileServiceClient
		wm.FromID = "server"
		wm.ToType = robotc.WildMsg_eMonitorClient
		wm.ToID = "95"
		if msg == nil {
			return nil
		}
		wm.BodyType = "DDRGRPCService.RelyMsg"
		relyMsg := &robotc.RelyMsg{StrGUID: msgID}
		c, err := proto.Marshal(relyMsg)
		if err != nil {
			logx.Error(context.Background(), "wildMsgAck marshal failed", logx.Err(err))
			return nil
		}
		wm.BodyContent = c
		wm.StrGUID = msgID
		return wm
	}
	return nil
}

// PublishCMDRsp 发布消息的响应
func PublishCMDRsp(robotID string, msg *robotc.WildMsg) {
	msgID := msg.StrGUID
	if strings.HasPrefix(msgID, "waitmsg_") {
		pubsub.PublishCMDRsp(robotID, "95", "wildmsg", msgID, msg.GetBodyContent())
	}
}

// optEventSetHandle 操作事件处理
func optEventSetHandle(ctx context.Context, data []byte) {
	deviceLogs := []models.DeviceLog{}
	event := robotc.OptEventSet{}
	err := proto.Unmarshal(data, &event)
	if err != nil {
		return
	}
	optEvents := event.GetData()
	for _, optEvent := range optEvents {
		deviceLog := models.DeviceLog{}
		deviceLog.RobotID = GetRobotID(ctx)
		deviceLog.LogType = int(optEvent.GetEventName())
		deviceLog.LogCategory = int(optEvent.GetType())
		// 获取日志详情
		optEventDetailBytes := optEvent.GetDataSet()
		// 保存日志详情
		deviceLog.Content = string(optEventDetailBytes)
		// 定义一个结构体，解析出核心的时间戳
		var jsonStruct struct {
			Timestamp int64 `json:"Timestamp"`
		}
		err := json.Unmarshal(optEventDetailBytes, &jsonStruct)
		if err != nil {
			deviceLog.CreatedAt = jsonStruct.Timestamp
		}
		deviceLog.CreatedDate = time.Now().Format("2006-01-02")
		deviceLog.CreatedMonth = time.Now().Format("2006-01")
		deviceLogs = append(deviceLogs, deviceLog)
	}
	// 批量插入数据库
	device.AddDeviceLog(deviceLogs)
}

// notifyMapListMsgHandle 地图列表上报处理
func notifyMapListMsgHandle(ctx context.Context, data []byte) {
	robotID := GetRobotID(ctx)
	mapList := &robotc.NotifyMapListMsg{}
	proto.Unmarshal(data, mapList)
	db := mysql.NewDB()
	var maps []models.DeviceMap

	dbs := db_sharding.GetDB(context.Background(), db, models.DeviceHeatMapTpl{}, time.Now().Unix()-3600*24*30*3, time.Now().Unix())
	// 热力图在使用的地图
	var mapIDs []string
	dbs.QueryDB.
		Where("robot_id = ?", robotID).
		Distinct().
		Pluck("map_id", &mapIDs)

	// 已经不使用的地图，但热力图数据还在引用的，做删除标记
	db.Model(&models.DeviceMap{}).
		Where("robot_id = ?", robotID).
		Where("map_name NOT IN ?", mapList.StrMapName).
		Where("id IN ?", mapIDs).
		Update("deleted_at", time.Now().Unix())

	// 不在地图列表中的地图且未被热力图使用的要删除
	db.Model(&models.DeviceMap{}).
		Where("robot_id = ?", robotID).
		Where("map_name NOT IN ?", mapList.StrMapName).
		Where("id NOT IN ?", mapIDs).
		Delete(&maps)

	// 地图文件清理
	{

		names := file.GetRobotAllMapFileName(config.Config.FileSaveDir, robotID, "png")
		for _, name := range names {
			var count int64
			db.Model(&models.DeviceMap{}).
				Where("robot_id = ?", robotID).
				Where("map_md5 = ?", name).
				Count(&count)
			if count == 0 {
				file.DeleteMapFile(config.Config.FileSaveDir, robotID, name, "png")
			}
		}

	}
	// 地图扩展文件清理
	{
		names := file.GetRobotAllMapFileName(config.Config.FileSaveDir, robotID, "ext")
		for _, name := range names {
			var count int64
			db.Model(&models.DeviceMap{}).
				Where("robot_id = ?", robotID).
				Where("map_ex_md5 = ?", name).
				Count(&count)
			if count == 0 {
				file.DeleteMapFile(config.Config.FileSaveDir, robotID, name, "ext")
			}
		}
	}
}

// alarmSettingHandle 告警通知配置处理
func alarmSettingHandle(ctx context.Context, data []byte) {
	var (
		notifySettings []models.AlarmSetting
		alarmSetConfig = &robotc.AlarmSetConfig{}
	)
	// 判断设备id是否存在
	robotID := GetRobotID(ctx)
	if robotID == "" {
		return
	}
	// 反序列化告警配置数据
	err := proto.Unmarshal(data, alarmSetConfig)
	if err != nil {
		return
	}
	// 循环处理告警通知配置
	for _, alarmSet := range alarmSetConfig.AlarmSet {
		notifySettings = append(notifySettings, models.AlarmSetting{
			RobotID:   robotID,
			AlarmType: alarmSet.GetAlarmConfigType(),
			Setting:   base64.StdEncoding.EncodeToString(alarmSet.GetBodyContent()),
		})
	}
	if len(notifySettings) > 0 {
		// 当告警规则少于预定规则时下发预定规则
		if len(notifySettings) < len(AlarmSetConfig.AlarmSet) {
			r := rand.New(rand.NewSource(time.Now().UnixNano()))
			randSecond := 30 + r.Intn(150) // 随机一个秒
			time.AfterFunc(time.Second*time.Duration(randSecond), func() {
				// 下发设置指令
				alarmSetConfigBytes, _ := proto.Marshal(AlarmSetConfig)
				pubsub.LocalMsgPublish(notifySettings[0].RobotID, "95", "wildmsg",
					string(AlarmSetConfig.ProtoReflect().Descriptor().FullName()),
					alarmSetConfigBytes,
					"",
				)
			})
		}
		alarm_service.SyncAlarmSetting(notifySettings)
	}
}

// getAlarmNotifyInfo 获取告警通知信息
func getAlarmNotifyInfo(alarmConfigType string, bodyContent []byte) (duration int32) {
	switch alarmConfigType {
	case "AlarmInfoProto.AlarmSetConfigConditionEmbError":
		alarm := &robotc.AlarmSetConfigConditionEmbError{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionLowBatt":
		alarm := &robotc.AlarmSetConfigConditionLowBatt{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionLossOfPositioning":
		alarm := &robotc.AlarmSetConfigConditionLossOfPositioning{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionBackCharge":
		alarm := &robotc.AlarmSetConfigConditionBackCharge{}
		proto.Unmarshal(bodyContent, alarm)
		return 0
	case "AlarmInfoProto.AlarmSetConfigConditionEmergencyStop":
		alarm := &robotc.AlarmSetConfigConditionEmergencyStop{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionIdling":
		alarm := &robotc.AlarmSetConfigConditionIdling{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionForcedTurn":
		alarm := &robotc.AlarmSetConfigConditionForcedTurn{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionRadarOcclusion":
		alarm := &robotc.AlarmSetConfigConditionRadarOcclusion{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionRFID":
		alarm := &robotc.AlarmSetConfigConditionRFID{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionElevator":
		alarm := &robotc.AlarmSetConfigConditionElevator{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionScreen":
		alarm := &robotc.AlarmSetConfigConditionScreen{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionOffline":
		alarm := &robotc.AlarmSetConfigConditionOffline{}
		proto.Unmarshal(bodyContent, alarm)
		return 0
	case "AlarmInfoProto.AlarmSetConfigConditionCurrOdometer":
		alarm := &robotc.AlarmSetConfigConditionCurrOdometer{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionUltrasound":
		alarm := &robotc.AlarmSetConfigConditionUltrasound{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionAlgorithmSingleradar":
		alarm := &robotc.AlarmSetConfigConditionAlgorithmSingleradar{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionAlgorithmVirtualwall":
		alarm := &robotc.AlarmSetConfigConditionAlgorithmVirtualwall{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionAlgorithmRGBD":
		alarm := &robotc.AlarmSetConfigConditionAlgorithmRGBD{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionReqPausePeople":
		alarm := &robotc.AlarmSetConfigConditionReqPausePeople{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionReqPauseAiServerHuman":
		alarm := &robotc.AlarmSetConfigConditionReqPauseAiServerHuman{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionReqPauseAiServerSell":
		alarm := &robotc.AlarmSetConfigConditionReqPauseAiServerSell{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionPositionStatic":
		alarm := &robotc.AlarmSetConfigConditionPositionStatic{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionStopChargingException":
		alarm := &robotc.AlarmSetConfigConditionStopChargingException{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionEmergencyStopResetkey":
		alarm := &robotc.AlarmSetConfigConditionEmergencyStopResetkey{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionAntiCollision":
		alarm := &robotc.AlarmSetConfigConditionAntiCollision{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	case "AlarmInfoProto.AlarmSetConfigConditionSIEGE":
		alarm := &robotc.AlarmSetConfigConditionSIEGE{}
		proto.Unmarshal(bodyContent, alarm)
		return alarm.GetDuration()
	}
	return 0
}

// 定时获取设备版本信息
func getSystemVersion(robotID string) {
	time.AfterFunc(time.Second*2, func() {
		cmd := &robotc.ReqGetSysVersion{}
		msgFullName := string(cmd.ProtoReflect().Descriptor().FullName())
		bodyContent, err := proto.Marshal(cmd)
		if err != nil {
			logx.Error(context.Background(), "getSystemVersion", logx.Err(err))
		}
		pubsub.LocalMsgPublish(robotID, "95", "wildmsg", msgFullName, bodyContent, "")
	})
}

// systemVersionHandle 处理版本上报信息
func systemVersionHandle(ctx context.Context, msg *robotc.WildMsg) {
	redisCtx, redisCtxCancel := context.WithTimeout(context.Background(), time.Second*3)
	defer redisCtxCancel()
	_, err := redis.NewDB().HSet(redisCtx, define.RedisRobotRTInfo+GetRobotID(ctx), "system_version", msg.GetBodyContent()).Result()
	if err != nil {
		logx.Error(context.Background(), "systemVersionHandle save system_version error", logx.Err(err))
	}
	// 保存硬件的固件版本
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	second := r.Intn(30)
	time.AfterFunc(time.Second*time.Duration(second), func() {
		rsp := &robotc.RspGetSysVersion{}
		proto.Unmarshal(msg.GetBodyContent(), rsp)
		versionItems := rsp.GetSysInfo()
		for _, item := range versionItems {
			if int(item.GetType()) == 0 {
				version := string(item.GetVersion())
				mysql.NewDB().Model(&models.Device{}).
					Where("robot_id = ?", GetRobotID(ctx)).
					Update("firmware_version", version)
				// 更新版本信息及升级结果
				{
					terminal.UpdateTerminalVersion(GetRobotID(ctx), "95", version)
					terminal.UpdateTerminalUpgradeResult(GetRobotID(ctx), "95", version)
				}
				break
			}
		}
	})
}

// InitMapInfo 初始化地图缓存信息
func InitMapCacheInfo(robotID string) {
	var mapRecords []models.DeviceMap
	mysql.NewDB().Model(&models.DeviceMap{}).
		Where("robot_id = ?", robotID).
		Where("deleted_at = ?", 0).
		Find(&mapRecords)
	for _, mapRecord := range mapRecords {
		func() {
			cache.HSet(define.CacheRobotRTInfo+robotID, "current_map_src_"+mapRecord.MapName, path.Join("/file/device-admin/device-map", robotID, mapRecord.MapMd5+".png"), 0, nil)
			affineData := &robotc.AffineMat{}
			affineData.R11 = mapRecord.R11
			affineData.R12 = mapRecord.R12
			affineData.R21 = mapRecord.R21
			affineData.R22 = mapRecord.R22
			affineData.Tx = mapRecord.Tx
			affineData.Ty = mapRecord.Ty
			dataBytes, _ := proto.Marshal(affineData)
			cache.HSet(define.CacheRobotRTInfo+robotID, mapRecord.MapName+"_affine_data", dataBytes, 0, nil)
		}()
	}
}
